#include <iostream>
#include <queue>
#include <pthread.h>

namespace lxp
{

    static const int gcap = 10;
    template <typename T>
    class BlockQueue
    {
        bool IsFull()
        {
            return _q.size() == _cap;
        }
        bool IsEmpty()
        {
            return _q.empty();
        }

    public:
        BlockQueue(int _cap = gcap) : _cap(gcap)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productor_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
        }
        void Equeue(const T &in)
        {
            pthread_mutex_lock(&_mutex);
            if (IsFull())
            {
                std::cout << "生产者进入等待..." << std::endl;
                _pwait_num++;
                pthread_cond_wait(&_productor_cond, &_mutex);
                _pwait_num--;

                std::cout << "生产者被唤醒" << std::endl;
            }
            if (_cwait_num)
            {
                std::cout << "唤醒消费者" << std::endl;
                pthread_cond_signal(&_consumer_cond);
            }
            _q.push(in);

            pthread_mutex_unlock(&_mutex);
        }

        void pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            if (IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond, &_mutex);
                _cwait_num--;

                std::cout << "消费者被唤醒" << std::endl;
            }
            if (_pwait_num)
            {
                std::cout << "唤醒生产者" << std::endl;
                pthread_cond_signal(&_productor_cond);
            }
            *out = _q.front();
            _q.pop();
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        std::queue<T> _q;
        int _cap;
        pthread_mutex_t _mutex;
        pthread_cond_t _productor_cond;
        pthread_cond_t _consumer_cond;

        int _cwait_num;
        int _pwait_num;
    };

}